package com.o2opuzi.lib.db;

import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;

public abstract class SupportDao<M extends Model<M, Integer>> {
	
	@Autowired
	public JdbcTemplate jdbcTemplate;
	
	
	/**
	 * 获取当前model的class
	 * @auther Y.hj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private synchronized  Class<M> getMClass(){
		return (Class<M>) (((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
	}
	
	/**
	 * 创建一个新实例
	 * @auther Y.hj
	 * @return
	 */
	public synchronized M createModel(){
		try {
			return getMClass().newInstance();
		} catch (Exception e) {
			throw new DbException(e);
		}
	}
	
	public M createModel(Terms terms){
		return createModel().where().addTermArray(terms);
	}
	
	
	public List<M> findList(M m){
		return jdbcTemplate.query(m.createQuerySQL(), new BeanPropertyRowMapper<M>(m.getEnClass()),m.params.toArray());
	}
	
	public List<Map<String, Object>> findListForMap(M m){
		return jdbcTemplate.queryForList(m.createQuerySQL(), m.params.toArray());
	}
	
	
	public M find(M m){
		List<M> list = findList(m);
		if(list.size() > 0){
			return list.get(0);
		}
		return null;
	}
	
	public M find(Terms terms){
		return find(createModel(terms));
	}
	
	public M find(ExcludeColumns excludeColumns){
		return find(createModel().excludeCols(excludeColumns.getColumns()));
	}
	
	public M find(Columns columns){
		return find(createModel().cols(columns.getColumns()));
	}
	
	public Map<String, Object> findForMap(M m){
		List<Map<String,Object>> list = findListForMap(m);
		if(list.size() > 0){
			return list.get(0);
		}
		return null;
	}
	
	
	
	/**
	 * 
	 * @auther Y.hj
	 * @param id
	 * @return
	 */
	public M findById(Integer id){
		return  find(new Terms(Arrays.asList((Object)"id","=",id)));
	}
	
	public Map<String, Object> findForMap(Integer id){
		return findForMap(createModel().where().eq("id", id));
	}
	
	/**
	 * 通过条件获取
	 * @auther Y.hj
	 * @param where
	 * @param params
	 * @return
	 */
	public M findBy(String where,Object...params){
		return find(createModel().where(where, params));
	}
	
	public List<M> findList(Integer page,Integer size){
		return findList(createModel().limit(page, size));
	}
	
	public List<M> findList(){
		return findList(createModel());
	}
	
	public List<Map<String, Object>> findListForMap(){
		return findListForMap(createModel());
	}
	
	public <T> T uniqueResult(Class<T> typeClass,M m){
		List<T> list = jdbcTemplate.queryForList(m.createQuerySQL(), typeClass,m.params.toArray());
		if(list.size() > 0){
			return list.get(0);
		}
		return null;
	}
	
	
	
	public Integer getCount(M m){
		String id = m.getTableMapping().getId();
		String querySQL = m.createQuerySQL(new String[]{"count("+id+")"});
		return jdbcTemplate.queryForObject(querySQL, m.params.toArray(), Integer.class);
	}
	
	public Integer getCount(){
		return getCount(createModel());
	}
	
	
	/**
	 * 通过条件获取返回Map 集合
	 * @auther Y.hj
	 * @param where
	 * @param params
	 * @return
	 */
	public Map<String, Object> findMap(String where,Object...params){
		return findForMap(createModel().where(where, params));
	}
	
	
	public Integer delete(M m){
		return jdbcTemplate.update(m.createDeleteQuery(),m.params.toArray());
	}
	
	/**
	 * 通过id删除
	 * @auther Y.hj
	 * @param id
	 * @return
	 */
	public Integer deleteById(Integer id){
		return deleteBy("id = ?", id);
	}
	
	/**
	 * 通过条件删除
	 * @auther Y.hj
	 * @param where
	 * @param params
	 * @return
	 */
	public Integer deleteBy(String where,Object...params){
		return delete(createModel().where(where, params));
	}
	
	public Integer deleteIn(List<Integer> idArray){
		return delete(createModel().where().in("id", idArray.toArray()));
	}
	
	/**
	 * 通过id更新
	 * @auther Y.hj
	 * @param model
	 * @return
	 */
	public Integer update(M m){
		return jdbcTemplate.update(m.createUpdateQuery(), m.params.toArray());
	}
	
	public Integer update(Map<String, Object> modelMap,Integer id){
		M m = createModel().where();
		TableMapping mapping = m.getTableMapping();
		m.where().eq(mapping.getId(),id);
		return jdbcTemplate.update(m.createUpdateQuery(modelMap),m.params.toArray());
	}
	
	public Integer updateById(M m){
		TableMapping mapping = m.getTableMapping();
		m.where().eq(mapping.getId(),mapping.getPropertyValue(mapping.getIdProperty(), m));
		return update(m);
	}
	
	
	/**
	 * 新增
	 * @auther Y.hj
	 * @param m
	 * @return
	 */
	public Integer save(M m){
		Map<String, Object> modelMap = new LinkedHashMap<String, Object>();
		TableMapping table = m.getTableMapping();
		List<PropertyDescriptor> columns = table.getColumns();
		for (PropertyDescriptor pd : columns) {
			String name = pd.getName();
			Object value = table.getPropertyValue(pd, m);
			if(value != null){
				modelMap.put(name, value);
			}
		}
		return save(modelMap);
	}
	
	/**
	 * 新增
	 * @auther Y.hj
	 * @param map
	 * @return
	 */
	public Integer save(Map<String, Object> map){
		M m = createModel();
		GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
		PreparedStatementCreator creator = new InsertPreparedStatementCreator(m.createInsertQuery(map), m.params.toArray());
		jdbcTemplate.update(creator, keyHolder);
		return Converter.convert(Integer.class, keyHolder.getKey().toString());
	}
	
	public Boolean exists(String filed,Object value){
		return getCount(createModel().where().eq(filed, value)) > 0;
	}
	
	public Page<M> findPage(Integer page,Integer size){
		Page<M> data = new Page<M>();
		data.setPage(page);
		data.setSize(size);
		data.setTotal(getCount());
		data.setData(findList(page,size));
		return data;
	}
	 
	
	public Page<M> findPage(M m,Integer page,Integer size){
		Page<M> data = new Page<M>();
		data.setPage(page);
		data.setSize(size);
		data.setTotal(getCount(m));
		data.setData(findList(m.limit(page, size)));
		return data;
	}
	
	public Page<Map<String, Object>> findPageMap(Integer page,Integer size){
		Page<Map<String, Object>> data = new Page<Map<String, Object>>();
		data.setPage(page); 
		data.setSize(size);
		data.setTotal(getCount());
		data.setData(findListForMap(createModel().limit(page, size)));
		return data;
	}
	
	public Page<Map<String, Object>> findPageMap(M m,Integer page,Integer size){
		Page<Map<String, Object>> data = new Page<Map<String, Object>>();
		data.setPage(page); 
		data.setSize(size);
		data.setTotal(getCount(m));
		data.setData(findListForMap(m.limit(page, size)));
		return data;
	}
	
}
